package hirt;

import java.util.Vector;
import java.util.Random;

import com.puzzud.engine.video.*;
import com.puzzud.engine.audio.*;
import com.puzzud.engine.input.*;
import com.puzzud.engine.utility.GameShell;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.util.LinkedList;

/*
 * @author adieffe
 *
 * GameScenePanel extends a very portable game engine shell, ultimately extending
 * from a JPanel. It acts as the main view of the scene in the HIRT simulator.
 * Mouse dragging internally and out of of GameScenePanel is handled within.
 * Mouse clicking and dragging in can also be interfaced from this class.
 */
public class GameScenePanel extends GameShell
{
	VideoDisplay videoDisplay;
	
	InputKeyboard keyboard;
	InputMouse mouse;

        // Graphics: images and sprites.
        VideoImage backGroundImage;
        VideoSprite leftLaneSprite, middleLaneSprite, rightLaneSprite, shoulderSprite;
	Vector<VideoSprite> carSpriteList;
        VideoImage grassImage;
        VideoSprite coneSprite;
        VideoSprite signSprite;
        VideoSprite drumSprite;
        VideoSprite flareSprite;
        VideoSprite lightSprite;
        VideoImage fireAnimationImage;

        /**
         * Light frame indexes.
         */
        public static int   FRAME_LIGHT_OFF             = 0;
        public static int   FRAME_LIGHT_CAUTION         = 1;
        public static int   FRAME_LIGHT_LEFT            = 2;
        public static int   FRAME_LIGHT_RIGHT           = 3;
        public static int   NUMBER_OF_FRAMES            = 4;

        /**
         * Light frame and animation data.
         */
        int                 LightOffAnimation[]         = { FRAME_LIGHT_OFF, FRAME_LIGHT_OFF, -1 };
        int                 LightCautionAnimation[]     = { FRAME_LIGHT_CAUTION, FRAME_LIGHT_OFF, -1 };
        int                 LightLeftAnimation[]        = { FRAME_LIGHT_LEFT, FRAME_LIGHT_OFF, -1 };
        int                 LightRightAnimation[]       = { FRAME_LIGHT_RIGHT, FRAME_LIGHT_OFF, -1 };

        /**
         * Light frame and animation indexes. (will probably move or adapt to server constants).
         */
        public static int   ANIMATION_LIGHT_OFF         = ServerConstants.LIGHT_STATE_OFF;
        public static int   ANIMATION_LIGHT_CAUTION     = ServerConstants.LIGHT_STATE_CAUTION;
        public static int   ANIMATION_LIGHT_LEFT        = ServerConstants.LIGHT_STATE_LEFT_ARROW;
        public static int   ANIMATION_LIGHT_RIGHT       = ServerConstants.LIGHT_STATE_RIGHT_ARROW;
        public static int   NUMBER_OF_LIGHT_ANIMATIONS  = 4;

        /**
         * Color indexes (Ode to Commodore 64).
         */
	public static final int COLOR_BLACK		= 0;
	public static final int COLOR_WHITE		= 1;
	public static final int COLOR_RED		= 2;
	public static final int COLOR_CYAN		= 3;
	public static final int COLOR_PURPLE		= 4;
	public static final int COLOR_GREEN		= 5;
	public static final int COLOR_BLUE		= 6;
	public static final int COLOR_YELLOW		= 7;
	public static final int COLOR_ORANGE		= 8;
	public static final int COLOR_BROWN		= 9;
	public static final int COLOR_LIGHT_RED		= 10;
	public static final int COLOR_DARK_GREY		= 11;
	public static final int COLOR_GREY		= 12;
	public static final int COLOR_LIGHT_GREEN	= 13;
	public static final int COLOR_LIGHT_BLUE	= 14;
	public static final int COLOR_LIGHT_GREY	= 15;

        /**
         * Color values (Ode to Commodore 64).
         */
	float[][] colorList =
	{
		new float[] { 0.0f, 0.0f, 0.0f },	// Black
		new float[] { 1.0f, 1.0f, 1.0f },	// White
		new float[] { 0.5f, 0.0f, 0.0f },	// Red
		new float[] { 0.7f, 1.0f, 0.9f },	// Cyan
		new float[] { 0.6f, 0.1f, 0.9f },	// Purple
		new float[] { 0.0f, 0.8f, 0.3f },	// Green
		new float[] { 0.0f, 0.0f, 0.7f },	// Blue
		new float[] { 0.9f, 0.9f, 0.5f },	// Yellow
		new float[] { 0.9f, 0.5f, 0.3f },	// Orange
		new float[] { 0.4f, 0.1f, 0.0f },	// Brown
		new float[] { 1.0f, 0.5f, 0.5f },	// Light Red
		new float[] { 0.2f, 0.2f, 0.2f },	// Dark Grey
		new float[] { 0.5f, 0.5f, 0.5f },	// Grey
		new float[] { 0.7f, 1.0f, 0.4f },	// Light Green
		new float[] { 0.0f, 0.5f, 1.0f },	// Light Blue
		new float[] { 0.7f, 0.7f, 0.7f }	// Light Grey
	};

	//Random randomGenerator = new Random();

        /**
         * State set when it is necessary to redraw the more stationary/occasional
         * background graphics (currently only the grass). Doing so saves on
         * graphics computation time (assumedly).
         */
        boolean backGroundNeedsRendering;
        
        /**
         * These are cars that are not vital (cars in traffic).
         */
        int numberOfOtherCars = 6;

        /**
         * Game engine representation of the mouse cursor
         */
        MoveableItem mouseCursor;

        /**
         * Stores the offset of where an item is clicked (and dragged).
         * (ie, a mother dog dragged its pups by the neck).
         */
        Point mouseCursorAndItemDelta = new Point( 0,0 );

        /**
         * Main hirt client to associate with this game scene.
         */
        HirtClient client;

        /**
         * Main custructor simply associates itself with a client and makes a
         * game engine representation of the mouse cursor (for clicking/dragging).
         * @param client Main hirt client to associate with this game scene.
         */
        public GameScenePanel( HirtClient client )
        {
            super();
            
            mouseCursor = new MoveableItem();
            mouseCursor.setId( 0 );
            mouseCursor.setType( "cursor" );
            mouseCursor.setWidth( (int)ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.CURSOR].getX() );
            mouseCursor.setHeight( (int)ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.CURSOR].getY() );
            mouseCursor.setPosition( 0, 0 );

            this.client = client;
        }

        /**
         * initializeVideoDisplay defines an abstract class to set the internal
         * dimensions of the video display (windowed).
         */
	public VideoDisplay initializeVideoDisplay()
	{
            videoDisplay = new VideoDisplay( 500, 850, this );
            return( videoDisplay );
	}

        /**
         * initialize graphics and such. Gets local references to the keyboard
         * and mouse. Called when GameScenePanel is first created.
         */
        public void initialize()
	{
            initializeGraphics();
            initializeScene();

            //client.getGUI().mainScreenPanel.refreshItems();

            keyboard = getKeyboard();
            mouse = getMouse();
	}

        /**
         * initializeLanes goes through all the lanes in the current scenario
         * and fits them to the correct video display dimensions. NOTE: This
         * is a bad idea (further development would be to have the server do
         * this while the Scenario world has dimensions which it currently
         * does not).
         */
        public void initializeLanes()
        {
            // Go through all the lanes and fit them.
            int index;
            Item2D world = client.getSession().getCurrentScenario().getWorld();
            LinkedList<Item> itemList = world.getItemList();

            // Traverse the list of visible items backwards (so topmost item is always returned first).
            for( index = itemList.size() - 1; index >= 0; index-- )
            {
                Item2D item2d = ( Item2D )itemList.get( index );

                if( item2d instanceof Lane )
                {
                    // Fit this lane to match the size of the videoDisplay.
                    // NOTE: Very sloppy joe and processor wasteful, TODO This better.
                    Lane lane = ( Lane )item2d;
                    lane.fit( videoDisplay.getRealWidth(), videoDisplay.getRealHeight() );
                }
            }
        }

        /**
         * Code that is called before the GameScenePanel is closed.
         */
	public void shutdown()
	{
            // Nothing for now.
	}

        /**
         * loop is called repeatly on GameScenePanel's thread. Use this for basic
         * game logic. Currently, the client 'listens' here. This should probably
         * be moved to its own thread within the network client class.
         */
	public void loop()
	{
            //doGeneralKeyboardInput();
            doMouseInput();
            lightSprite.animate();

            // NOTE: This should probably not be here.
            client.listen();
	}

        /**
         * Act on keyboard input.
         */
	public void doGeneralKeyboardInput()
	{
            /*
            if( keyboard.getKeyState( InputKeyboard.KEY_SPACE ) == InputKeyboard.KEY_STATE_PRESSED )
            {

            }

            if( keyboard.getKeyState( InputKeyboard.KEY_ENTER ) == InputKeyboard.KEY_STATE_PRESSED )
            {

            }
            */
	}

        /**
         * Act on mouse input.
         */
        public void doMouseInput()
	{
            // Get Mouse position with respect to possible window scaling
            mouseCursor.setXPosition( ( mouse.getXPosition() * videoDisplay.getRealWidth()  ) / videoDisplay.getWidth() );
            mouseCursor.setYPosition( ( mouse.getYPosition() * videoDisplay.getRealHeight() ) / videoDisplay.getHeight() );

            // Handle clicks and drags.
            doMouseInputLeftClick();
            doMouseInputDrag();
            doMouseInputRightClick();
	}

        /**
         * Act on mouse left clicks (includes presses and releases).
         */
        public void doMouseInputLeftClick()
        {
            // Handle left mouse click release.
            if( ( mouse.getButtonState( 0 ) == InputMouse.BUTTON_STATE_PRESSED ) )
            {
                doMouseInputLeftClickPress();
            }
            else // Handle left mouse click release.
            if( ( mouse.getButtonState( 0 ) == InputMouse.BUTTON_STATE_RELEASED ) )
            {
                doMouseInputLeftClickRelease();
            }
        }

        /**
         * Action code for left click presses.
         */
        public void doMouseInputLeftClickPress()
        {
            Item2D item;
            MainFrame mainFrame = client.getGUI();

            // Retrieve the 'game' Item2D the user is left clicking on.
            item = getClickedItem( 0 );

            if( item == null )
            {
                // Do nothing?
                return;
            }
            else
            if( item instanceof Lane )
            {
                // If this is a Lane.
                int mode = client.getSession().getMode();
                if( mode == Session.MODE_EDITOR )
                {
                    mouseCursorAndItemDelta.x = mouseCursor.getXPosition() - item.getXPosition();
                    mouseCursorAndItemDelta.y = mouseCursor.getYPosition() - item.getYPosition();
                    mainFrame.mouseLeftClickedItemReference = item;
                    mainFrame.mouseDraggedItemReference = item;
                }
                else
                {
                    // Do nothing in Session.MODE_PLAYER;
                    return;
                }
            }
            else
            if( item instanceof Automobile )
            {
                // If this is an Automobile and is owned by the localPlayer,
                // set it as the currentCar.
                Player localPlayer = client.getLocalPlayer();

                if( localPlayer == null )
                {
                    System.out.println( "LOCAL PLAYER IS NULL WHEN CLICKING AUTOMOBILE" );
                }
                else
                {
                    Automobile currentCar = ( Automobile )item;

                    if( client.getSession().getMode() == Session.MODE_EDITOR )
                    {
                        System.out.println( "EDITOR MODE SELECTED CAR WITH INSTANCE #" + currentCar.getInstance() );
                        client.getLocalPlayer().setCurrentCar( currentCar );

                        mouseCursorAndItemDelta.x = mouseCursor.getXPosition() - item.getXPosition();
                        mouseCursorAndItemDelta.y = mouseCursor.getYPosition() - item.getYPosition();
                        mainFrame.mouseLeftClickedItemReference = item;
                        mainFrame.mouseDraggedItemReference = item;

                        // Send request to update GUI to reflect change of car.
                        client.getGUI().mainScreenPanel.refreshVehicle();
                        client.getGUI().mainScreenPanel.refreshItems();
                    }
                    else
                    if( client.getSession().getMode() == Session.MODE_PLAY )
                    {
                        if( localPlayer.getCarList().contains( currentCar ) )
                        {
                            client.getLocalPlayer().setCurrentCar( currentCar );

                            mouseCursorAndItemDelta.x = mouseCursor.getXPosition() - item.getXPosition();
                            mouseCursorAndItemDelta.y = mouseCursor.getYPosition() - item.getYPosition();
                            mainFrame.mouseLeftClickedItemReference = item;
                            mainFrame.mouseDraggedItemReference = item;

                            // Send request to update GUI to reflect change of car.
                            client.getGUI().mainScreenPanel.refreshVehicle();
                            client.getGUI().mainScreenPanel.refreshItems();
                        }
                    }
                }
            }
            else
            if( item instanceof Item2D )
            {
                mouseCursorAndItemDelta.x = mouseCursor.getXPosition() - item.getXPosition();
                mouseCursorAndItemDelta.y = mouseCursor.getYPosition() - item.getYPosition();
                mainFrame.mouseDraggedItemReference = item;
                mainFrame.mouseDraggedItemReference = item;
            }
        }

        /**
         * Action code for left click releases.
         */
        public void doMouseInputLeftClickRelease()
        {
            MainFrame mainFrame = client.getGUI();
            Automobile currentCar;

            // Handle end of a dragging item, if it occurred.
            if( mainFrame.mouseDraggedItemReference != null )
            {
                if( mouse.isDragEnded() )
                {
                    Player localPlayer = client.getLocalPlayer();

                    // Make sure there is a valid local player and current car.
                    if( localPlayer == null )
                    {
                        return;
                    }
                    else
                    {
                        if( client.getSession().getMode() == Session.MODE_PLAY )
                        {
                            currentCar = localPlayer.getCurrentCar();

                            if( currentCar == null )
                            {
                                return;
                            }
                        }
                    }

                    // Reference the dragged item.
                    Item2D draggedItem = ( Item2D )mainFrame.mouseDraggedItemReference;

                    // Declare endpoints of drag.
                    Point origin = new Point( mouse.getXDragOrigin(), mouse.getYDragOrigin() );
                    origin.x = mouse.getXDragOrigin() - mouseCursorAndItemDelta.x;
                    origin.y = mouse.getYDragOrigin() - mouseCursorAndItemDelta.y;

                    Point endPoint = new Point();
                    endPoint.x = mouseCursor.getXPosition() - mouseCursorAndItemDelta.x;
                    endPoint.y = mouseCursor.getYPosition() - mouseCursorAndItemDelta.y;

                    int outOfBoundsSide = -1;

                    // Check conditions for end of drag:
                    if( endPoint.x > videoDisplay.getRealWidth() )
                    {
                        if( !( draggedItem instanceof Lane ) )
                        {
                             outOfBoundsSide = 1;
                        }
                    }
                    else
                    if( endPoint.x < 0 )
                    {
                        if( !( draggedItem instanceof Lane ) )
                        {
                            outOfBoundsSide = 2;
                        }
                    }
                    else
                    if( endPoint.y < 0 )
                    {
                        if( !( draggedItem instanceof Lane ) )
                        {
                             outOfBoundsSide = 3;
                        }
                    }
                    else
                    if( endPoint.y > videoDisplay.getRealHeight() )
                    {
                        if( !( draggedItem instanceof Lane ) )
                        {
                            outOfBoundsSide = 4;
                        }
                    }

                    // Handle the drag based on whether it was in or out of bounds.
                    if( outOfBoundsSide > -1 )
                    {
                        // If dragged out to the right and it's not a car, put it back.
                        // If the item drag end beyond right side of scene.
                        if( !( draggedItem instanceof Automobile ) && ( outOfBoundsSide == 1 ) )
                        {
                            // This is not a car.
                            currentCar = localPlayer.getCurrentCar();

                            if( currentCar == null )
                            {
                                return;
                            }
                            else
                            {
                                // This is an item to put in the currentCar.
                                client.sendTransfer( draggedItem, draggedItem, currentCar );
                            }
                        }
                        else
                        {
                            // Otherwise, put it back into bounds.
                            origin.x = (int) (origin.x  / videoDisplay.getXScale());
                            origin.y = (int) (origin.y / videoDisplay.getYScale());
                        }
                    }
                    else
                    {
                        // Check for collision before sending out request to server.
                        Scenario currentScenario = client.getSession().getCurrentScenario();
                        if( currentScenario.detectCollision( draggedItem ) )
                        {
                            // Put it back.
                            origin.x = (int) (origin.x  / videoDisplay.getXScale());
                            origin.y = (int) (origin.y / videoDisplay.getYScale());
                            //draggedItem.setPosition( origin.x, origin.y );
                        }
                        else
                        {
                            // This is a seemingly "valid" item move, so send request to server.
                            client.moveItem( draggedItem, origin, endPoint );
                        }
                    }

                    // NOTE: Should this go here?
                    // Reset cursor/item drag offset.
                    mouseCursorAndItemDelta.x = 0;
                    mouseCursorAndItemDelta.y = 0;
                }

                mainFrame.mouseDraggedItemReference = null;
            }
        }

        /**
         * Action code for right clicks (only releases right now). Importantly,
         * passes the mainScrenePanel.handleSceneClick code, so the main
         * GUI can act on the clicking of items (in general).
         */
        public void doMouseInputRightClick()
        {
            Item2D item;

            if( ( mouse.getButtonState( 2 ) == InputMouse.BUTTON_STATE_RELEASED ) )
            {
                // Retrieve the 'game' Item2D the user is right clicking on.
                item = getClickedItem( 2 );
                client.getGUI().mouseRightClickedItemReference = item;

                // Send this information to the GUI for processing.
                client.getGUI().mainScreenPanel.handleSceneClick( item );
            }
        }

        /**
         * Action code for mouse dragged (left click specific).
         */
        public void doMouseInputDrag()
        {
            // Process dragging for left click drags (only for now).
            if( mouse.isDragging() && ( mouse.getButtonState( 0 ) == InputMouse.BUTTON_STATE_DOWN ) )
            {
                MainFrame mainFrame = client.getGUI();

                if( mainFrame.mouseLeftClickedItemReference != null )
                {
                    Item2D draggedItem = ( Item2D )mainFrame.mouseDraggedItemReference;

                    if( draggedItem == null )
                    {
                        // Do nothing, did not original click on an actual item.
                        return;
                    }
                    else
                    {
                        // Only allow in Session.MODE_EDITOR.
                        if( client.getSession().getMode() != Session.MODE_EDITOR )
                        {
                            // Determine if the player can move this item.
                            if( draggedItem instanceof Automobile )
                            {
                                if( draggedItem.getOwner() != client.getLocalPlayer() )
                                {
                                    return;
                                }
                            }
                            else
                            if( draggedItem instanceof Lane )
                            {
                                return;
                            }
                            else
                            {
                                // Basic Item2D.
                            }
                        }

                        // Dangerously let the user move the lane graphics in realtime (in EDITOR MODE).
                        if( draggedItem instanceof Lane )
                        {
                            draggedItem.setPosition( mouseCursor.getXPosition() - mouseCursorAndItemDelta.x,
                                                     mouseCursor.getYPosition() - mouseCursorAndItemDelta.y );
                        }
                    }
                }
            }
        }

        /**
         * Determine if the user is dragging or clicking a 'game' Item.
         * @param buttonNumber NOT USED (intended for handling which items are clicked on by mouse button number).
         * @return The Item2D that is being clicked on (returns a reference to Scenario.world if else).
         */
        public Item2D getClickedItem( int buttonNumber )
        {
            Item2D item2d;
            Scenario currentScenario = client.getSession().getCurrentScenario();

            // Make sure a session is active.
            if( currentScenario == null )
            {
                return( null );
            }
            
            // First check if it is an automobile.
            item2d = getClickedAutomobile();
            if( item2d != null )
            {
                return( item2d );
            }

            // If not, check if it is one of the many item2d's (if clicking anything at all).
            item2d = getClickedMiscItem();
            if( item2d != null )
            {
                return( item2d );
            }

            // If not, check if it is a lane.
            item2d = getClickedLane();
            if( item2d != null )
            {
                return( item2d );
            }

            System.out.println( "CLICKED WORLD" );

            // Else, (hopefully) just return the world.
            item2d = client.getSession().getCurrentScenario().getWorld();

            // NOTE: Do I nullify dragged item reference here?
            //client.getGUI().mouseDraggedItemReference = null;

            return( item2d );
        }

        /**
         * Handle whether clicking on an Automobile, basically a collision
         * detection between the mouseCursor Item2D.
         * @return left clicked Automobile.
         */
        public Automobile getClickedAutomobile()
        {
            int index;
            Item2D world = client.getSession().getCurrentScenario().getWorld();
            LinkedList<Item> itemList = world.getItemList();

            // Traverse the list of visible items backwards (so topmost item is always returned first).
            for( index = itemList.size() - 1; index >= 0; index-- )
            {
                Item2D item2d = ( Item2D )itemList.get( index );

                if( item2d instanceof Automobile )
                {
                    if( mouseCursor.detectCollision( item2d ) )
                    {
                        System.out.println( "CLICKED AUTOMOBILE" );
                        //System.out.println( "getClickedItem: " + item2d.getType() + ":" + item2d.getId() );
                        return ( ( Automobile )item2d );
                    }
                }
            }

            return( null );
        }

        /**
         * Handle whether clicking on an Lane, basically a collision
         * detection between the mouseCursor Item2D.
         * @return left clicked Lane.
         */
        public Lane getClickedLane()
        {
            int index;
            Item2D world = client.getSession().getCurrentScenario().getWorld();
            LinkedList<Item> itemList = world.getItemList();

            // Traverse the list of visible items backwards (so topmost item is always returned first).
            for( index = itemList.size() - 1; index >= 0; index-- )
            {
                Item2D item2d = ( Item2D )itemList.get( index );

                if( item2d instanceof Lane )
                {
                    // Fit this lane to match the size of the videoDisplay.
                    // NOTE: Very sloppy joe and processor wasteful, TODO This better.
                    Lane lane = ( Lane )item2d;
                    lane.fit( videoDisplay.getRealWidth(), videoDisplay.getRealHeight() );

                    if( mouseCursor.detectCollision( item2d ) )
                    {
                        System.out.println( "CLICKED LANE" );
                        //System.out.println( "getClickedItem: " + item2d.getType() + ":" + item2d.getId() );
                        return ( ( Lane )item2d );
                    }
                }
            }

            return( null );
        }

        /**
         * Handle whether clicking on an misc Item2D, basically a collision
         * detection between the mouseCursor Item2D.
         * @return left clicked Item2D (like a cone).
         */
        public Item2D getClickedMiscItem()
        {
            int index;
            Item2D world = client.getSession().getCurrentScenario().getWorld();
            LinkedList<Item> itemList = world.getItemList();

            // Traverse the list of visible items backwards (so topmost item is always returned first).
            for( index = itemList.size() - 1; index >= 0; index-- )
            {
                Item2D item2d = ( Item2D )itemList.get( index );

                if( !( item2d instanceof Automobile ) && !( item2d instanceof Lane ) )
                {
                    if( mouseCursor.detectCollision( item2d ) )
                    {
                        System.out.println( "CLICKED MISC ITEM" );
                        //System.out.println( "getClickedItem: " + item2d.getType() + ":" + item2d.getId() );
                        return ( item2d );
                    }
                }
            }

            return( null );
        }

        /*
	public int getRandomNumber( int min, int max )
	{
            return( randomGenerator.nextInt( max - min + 1 ) + min );
	}
        */

        /**
         * initializeGraphics sets up the graphics for the scene.
         */
        public void initializeGraphics()
        {
            videoDisplay = getVideoDisplay();
            videoDisplay.setFrameRate( 60 );
            videoDisplay.setScaleType( VideoObject2D.SCALE_TYPE_SMOOTH );
            videoDisplay.setBackgroundColor( colorList[COLOR_BLACK] );
            videoDisplay.setPrintColor( colorList[COLOR_YELLOW] );

            initializeCarGraphics();
            initializeLaneGraphics();
            initializeMiscGraphics();
            initializeLightGraphics();
        }

        /**
         * Set up the various graphics for misc items, like cones and fire animation.
         */
        public void initializeMiscGraphics()
        {
            // Load in grass graphic.
            grassImage = new VideoImage( this );
            grassImage.load( "/graphics/grass.png" );

            // Load in cone graphic/sprite.
            VideoImage coneImage = new VideoImage( this );
            coneImage.load( "/graphics/cone.png" );
            coneSprite = new VideoSprite( coneImage.getWidth(), coneImage.getHeight(), VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
            coneSprite.loadFrame( coneImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
            coneSprite.setDimensions( ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.CONE].x, ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.CONE].y );

            // Load in sign graphic/sprite.
            VideoImage signImage = new VideoImage( this );
            signImage.load( "/graphics/safetysign_50x50.png" );
            signSprite = new VideoSprite( signImage.getWidth(), signImage.getHeight(), VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
            signSprite.loadFrame( signImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
            signSprite.setDimensions( ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.SIGN].x, ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.SIGN].y );

            // Load in drum graphic/sprite.
            VideoImage drumImage = new VideoImage( this );
            drumImage.load( "/graphics/drum_50x50.png" );
            drumSprite = new VideoSprite( drumImage.getWidth(), drumImage.getHeight(), VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
            drumSprite.loadFrame( drumImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
            drumSprite.setDimensions( ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.DRUM].x, ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.DRUM].y );

            // Load in flare graphic/sprite.
            VideoImage flareImage = new VideoImage( this );
            flareImage.load( "/graphics/flares_50x50.png" );
            flareSprite = new VideoSprite( flareImage.getWidth(), flareImage.getHeight(), VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
            flareSprite.loadFrame( flareImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
            flareSprite.setDimensions( ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.FLARE].x, ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.FLARE].y );

            fireAnimationImage = new VideoImage( this );
            fireAnimationImage.load( "/graphics/fire_anim.gif" );
            fireAnimationImage.setDimensions( 32, 32 );

            // Initialize the background graphic surface.
            backGroundImage = new VideoImage( videoDisplay.getRealWidth(), videoDisplay.getRealHeight(), this );
        }

        /**
         * Initialize all of the lane graphics.
         */
        public void initializeLaneGraphics()
        {
            // Load in lane graphics.
            VideoImage laneImage;

            laneImage = new VideoImage( this );
            laneImage.load( "/graphics/left_lane.png" );
            laneImage.setDimensions( ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.VERTICAL_LANE].x, ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.VERTICAL_LANE].y );
            leftLaneSprite = new VideoSprite( laneImage.getWidth(), laneImage.getHeight(), VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
            leftLaneSprite.loadFrame( laneImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
            leftLaneSprite.setRotationAxis( 0, 0 );

            laneImage = new VideoImage( this );
            laneImage.load( "/graphics/middle_lane.png" );
            laneImage.setDimensions( ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.VERTICAL_LANE].x, ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.VERTICAL_LANE].y );
            middleLaneSprite = new VideoSprite( laneImage.getWidth(), laneImage.getHeight(), VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
            middleLaneSprite.loadFrame( laneImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
            middleLaneSprite.setRotationAxis( 0, 0 );

            laneImage = new VideoImage( this );
            laneImage.load( "/graphics/right_lane.png" );
            laneImage.setDimensions( ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.VERTICAL_LANE].x, ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.VERTICAL_LANE].y );
            rightLaneSprite = new VideoSprite( laneImage.getWidth(), laneImage.getHeight(), VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
            rightLaneSprite.loadFrame( laneImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
            rightLaneSprite.setRotationAxis( 0, 0 );

            laneImage = new VideoImage( this );
            laneImage.load( "/graphics/shoulder.png" );
            laneImage.setDimensions( ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.VERTICAL_SHOULDER].x, ServerConstants.ITEM2D_DIMENSIONS[ServerConstants.VERTICAL_SHOULDER].y );
            shoulderSprite = new VideoSprite( laneImage.getWidth(), laneImage.getHeight(), VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
            shoulderSprite.loadFrame( laneImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
            shoulderSprite.setRotationAxis( 0, 0 );
        }

        /**
         * Initialize the car graphics for each role.
         */
        public void initializeCarGraphics()
        {
            int carIndex;
            VideoImage carImage;
            VideoSprite carSprite;

            String imageFileLocation;
            int spriteWidth;
            int spriteHeight;

            // Initial master car sprite list.
            carSpriteList = new Vector<VideoSprite>();

            // TODO Perhaps do this better by creating arrays of this data instead of using case statments.

            // Load in car graphics for each role.
            for( carIndex = 0; carIndex < Role.NUMBER_OF_ROLES; carIndex++ )
            {
                switch( carIndex )
                {
                    case Role.EMS:
                    {
                        // Load in ambulance car graphic.
                        imageFileLocation = "/graphics/car_ambulance_64x128.png";
                        spriteWidth = 64;
                        spriteHeight = 128;
                        
                    } break;

                    case Role.FIRE:
                    {
                        // Load in firetruck car graphic.
                        imageFileLocation = "/graphics/car_firetruck_80x196.png";
                        spriteWidth = 80;
                        spriteHeight = 196;
                    } break;

                    case Role.POLICE:
                    {
                        // Load in police car graphic.
                        imageFileLocation = "/graphics/car_police_64x128.png";
                        spriteWidth = 64;
                        spriteHeight = 128;
                    } break;

                    case Role.IDOT:
                    {
                        // Load in idot car graphic.
                        imageFileLocation = "/graphics/car_idot_64x128.png";
                        spriteWidth = 64;
                        spriteHeight = 128;
                    } break;

                    case Role.TOW:
                    {
                        // Load in towtruck car graphic.
                        imageFileLocation = "/graphics/car_towtruck_64x144.png";
                        spriteWidth = 64;
                        spriteHeight = 144;
                    } break;

                    default:
                    {
                        // Load in standard car graphic.
                        imageFileLocation = "/graphics/car1_64x128.png";
                        spriteWidth = 64;
                        spriteHeight = 128;
                    } break;
                }

                carImage = new VideoImage( this );
                carImage.load( imageFileLocation );
                carSprite = new VideoSprite( spriteWidth, spriteHeight, VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
                carSprite.loadFrame( carImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
                carSprite.setDimensions( carSprite.getWidth() / 2, carSprite.getHeight() / 2 );

                carSpriteList.add( carSprite );
            }
            
            // Load in standard car graphic.
            carImage = new VideoImage( this );
            carImage.load( "/graphics/car1_64x128.png" );

            // Load in car sprites.
            for( carIndex = 0; carIndex < numberOfOtherCars; carIndex++ )
            {
                carSprite = new VideoSprite( 64, 128, VideoSprite.SPRITE_ATTR_SINGLE_FRAME, this );
                carSprite.loadFrame( carImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );
                carSprite.setDimensions( carSprite.getWidth() / 2, carSprite.getHeight() / 2 );

                carSpriteList.add( carSprite );
            }
        }

        /**
         * Initialize light graphics for the (service patrol right now).
         */
        public void initializeLightGraphics()
        {
            // Load in lane graphics.
            VideoImage lightImage;

            lightImage = new VideoImage( this );
            lightImage.load( "/graphics/light_off.png" );
            lightSprite = new VideoSprite( lightImage.getWidth(), lightImage.getHeight(), VideoSprite.SPRITE_ATTR_MULTI_ANIM, this );
            lightSprite.loadFrame( lightImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );

            lightImage = new VideoImage( this );
            lightImage.load( "/graphics/light_caution.png" );
            lightSprite.loadFrame( lightImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );

            lightImage = new VideoImage( this );
            lightImage.load( "/graphics/light_left.png" );
            lightSprite.loadFrame( lightImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );

            lightImage = new VideoImage( this );
            lightImage.load( "/graphics/light_right.png" );
            lightSprite.loadFrame( lightImage, 0, 0, VideoSprite.EXTRACT_MODE_ABS );

            // Setup animations
            lightSprite.loadAnimation( LightOffAnimation );
            lightSprite.loadAnimation( LightCautionAnimation );
            lightSprite.loadAnimation( LightLeftAnimation );
            lightSprite.loadAnimation( LightRightAnimation );

            lightSprite.setAnimation( ANIMATION_LIGHT_OFF );
            lightSprite.setAnimationSpeed( 20 );
            lightSprite.setAlpha( 0.75f );
        }

        /**
         * Basic function to call to initialize scene (draw graphics and fit
         * lanes).
         */
        public void initializeScene()
        {
            backGroundNeedsRendering = true;
            initializeLanes();
        }

        /**
         * GameScenePanel specific handling of when the client receives a
         * MovementAction.
         * @param movementAction action with information of what has moved.
         */
        public void receiveItemMove( MovementAction movementAction )
        {
            Item itemActor = movementAction.getActor();

            // TODO: For now, do not pay attention to the time.
            //double startTime = movementAction.getStartTime();
            //double getDurationTime = movementAction.getDurationTime();

            int index;
            Vector<Item> itemList = client.getSession().getItemList();

            for( index = 0; index < itemList.size(); index++ )
            {
                Item tempItem = itemList.get( index );

                if( itemActor == tempItem )
                {
                    Item2D tempItem2D = ( Item2D )itemActor;
                    tempItem2D.setPosition( movementAction.endPoint.x, movementAction.endPoint.y );
                    tempItem2D.setAngle( movementAction.angle );

                    // See if we have to fit this lane.
                    if( tempItem2D instanceof Lane )
                    {
                        Lane lane = ( Lane )tempItem2D;
                        lane.fit( videoDisplay.getRealWidth(), videoDisplay.getRealHeight() );
                    }
                }
            }
        }

        /**
         * GameScenePanel specific handling of when the client receives a
         * TransferAction.
         * @param transferAction
         */
        public void receiveItemTransfer( TransferAction transferAction )
        {
            //Item actor = transferAction.actor;
            Item reciever = transferAction.reciever;
            Item transferredItem = transferAction.moved;

            // Handle transfers to the visible world.
            if( reciever.getType().equals( "world" ) )
            {
                //System.out.print( "GameScenePanel.receiveItemTransfer(): receiver is world, ");

                // If this is a cone, we should probably show it.
                if(     transferredItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.CONE] ) ||
                        transferredItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.SIGN] ) ||
                        transferredItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.DRUM] ) ||
                        transferredItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.FLARE] ) )
                {
                    //System.out.println( "item# " + transferredItem.getId());
                    client.getGUI().mainScreenPanel.refreshItems();
                }
                else
                {
                    reciever.putItem(transferredItem);
                    if (transferredItem instanceof Automobile)
                    {
                        Automobile car = (Automobile) transferredItem;
                        Player player = client.getSession().getPlayerByRole(car.getRoleId());
                        if (player != null)
                            player.addCar(car);
                    }

                }
            }
            else
            {
                if(     transferredItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.CONE] ) ||
                        transferredItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.SIGN] ) ||
                        transferredItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.DRUM] ) ||
                        transferredItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.FLARE] ) )
                {
                    //System.out.println( "GameScenePanel.receiveItemTransfer(): item #" + transferredItem.getId());
                    client.getGUI().mainScreenPanel.refreshItems();
                }
            }
        }

        /**
         * GameScenePanel specific handling of when the client receives a
         * CreationAction.
         * @param creationAction
         */
        public void receiveItemCreation( CreationAction creationAction )
        {
            // If this is a lane, fit it to the display.
            if( creationAction.actor instanceof Lane )
            {
                Lane lane = ( Lane )creationAction.actor;
                lane.fit( videoDisplay.getRealWidth(), videoDisplay.getRealHeight() );
            }
        }

        /**
         * Code that called repeated on its own thread, used to draw everything
         * to the video display.
         */
        public void draw()
	{
            drawBackground( backGroundNeedsRendering );
            drawMiscItems();
            drawCars();
            drawDraggedItem();

            /*
            // DEBUG: Print cursor position coordinates on screen.
            videoDisplay.print( "(" + mouseCursor.getXPosition() + "," + mouseCursor.getYPosition() + ")",
                                mouseCursor.getXPosition(), mouseCursor.getYPosition() );
            */

            // Draw what mode it is (everything but play mode).
            Session session = client.getSession();
            if( session != null )
            {
                videoDisplay.setPrintSize( 64 );

                final float captionColorBlack[] =  new float[] { 0.0f, 0.0f, 0.0f, 0.25f };

                if( session.getMode() == Session.MODE_EDITOR )
                {
                    final float modeEditorCaptionColor[] =  new float[] { colorList[COLOR_YELLOW][0], colorList[COLOR_YELLOW][1], colorList[COLOR_YELLOW][2], 0.75f };
                    
                    videoDisplay.setPrintColor( captionColorBlack );
                    videoDisplay.print( "Editor Mode", 80, 64 );
                    videoDisplay.setPrintColor( modeEditorCaptionColor );
                    videoDisplay.print( "Editor Mode", 81, 65 );
                }
                else
                if( session.getMode() == Session.MODE_REVIEW )
                {
                    final float modeReviewCaptionColor[] =  new float[] { colorList[COLOR_LIGHT_RED][0], colorList[COLOR_BLUE][1], colorList[COLOR_BLUE][2], 0.75f };

                    videoDisplay.setPrintColor( captionColorBlack );
                    videoDisplay.print( "Review Mode", 80, 64 );
                    videoDisplay.setPrintColor( modeReviewCaptionColor );
                    videoDisplay.print( "Review Mode", 81, 65 );
                }
            }
	}

        /**
         * Method to initiate a one time redraw of the background.
         */
        public void updateBackground()
        {
            drawBackground( true );
        }

        /**
         * Method to draw the background.
         * @param render toggle switch to determine to actually draw or not.
         */
        public void drawBackground( boolean render )
        {
            if( render == true )
            {
                //Scenario currentScenario = client.getSession().getCurrentScenario();
                //Item2D world = currentScenario.getWorld();

                drawGrass();

                backGroundNeedsRendering = false;
                videoDisplay.draw( backGroundImage );
            }
            else
            {
                videoDisplay.draw( backGroundImage );
            }

            drawLanes();
        }

        /**
         * Draw grass for the background (draws to a background image, not directly
         * to the video display.
         */
        public void drawGrass()
        {
            int x, y;

            int numberOfTilesTall = ( videoDisplay.getRealHeight() / grassImage.getHeight() ) + 1;
            int numberOfTilesWide = ( videoDisplay.getRealWidth() / grassImage.getWidth() ) + 1;
            
            // Draw grass.
            for( y = 0; y < numberOfTilesTall; y++ )
            {
                grassImage.setYPosition( y * grassImage.getHeight() );
                
                for( x = 0; x < numberOfTilesWide; x++ )
                {
                    grassImage.setXPosition( x * grassImage.getWidth() );
                    backGroundImage.draw( grassImage );
                }
            }
        }

        /**
         * Draws lanes. Handles lane overlapping and such with area/shap intersections
         * and unions.
         */
        public void drawLanes()
        {
            Item2D world;
            Scenario currentScenario;

            currentScenario = client.getSession().getCurrentScenario();
            world = currentScenario.getWorld();
            LinkedList<Item> itemList = world.getItemList();

            int size = itemList.size();

            if( size == 0 )
            {
                //System.out.println( "drawLanes(): No lanes to draw." );
                return;
            }

            // Make a pass to create all lane "road" union.
            Area roadUnionArea = getLaneArithmetic( 0, 1 );

            Area verticalRoadUnionArea = getLaneArithmetic( 0, 3 );
            Area horizontalRoadUnionArea = getLaneArithmetic( 0, 4 );

            // Get just shoulders.
            Area shoulderUnionArea = getLaneArithmetic( 0, 2 );
            shoulderUnionArea.subtract( roadUnionArea );

            // Make a pass to create all lane "road" intersection.
            Area roadIntersectionArea = ( Area )verticalRoadUnionArea.clone();
            roadIntersectionArea.intersect( horizontalRoadUnionArea );

            videoDisplay.setPrintColor( colorList[COLOR_LIGHT_GREY] );
            videoDisplay.drawArea( shoulderUnionArea );

            videoDisplay.setPrintColor( colorList[COLOR_BLACK] );
            videoDisplay.drawArea( roadUnionArea );

            // Draw lane lines here.
            drawLaneLinesByType( Lane.TYPE_HORIZONTAL );
            drawLaneLinesByType( Lane.TYPE_VERTICAL );

            videoDisplay.setPrintColor( colorList[COLOR_BLACK] );
            videoDisplay.drawArea( roadIntersectionArea );
        }

        /**
         * Helper function that returns an area based on all the lanes, determined
         * by the 'which' parameter. The arithmeticType specifies how the lanes
         * are compared: union, intersection, subtraction. Once Scenario world
         * properly has dimension (not currently so), this code could be migrated
         * to the Scenario class.
         * @param arithmeticType which type of arithmetic to perform (1-3, union, intersection, subtraction).
         * @param which type of lanes to compare (1-4, see code).
         * @return the Area formed by the arithmetic of the lanes compared in the current Scenario.
         */
        public Area getLaneArithmetic( int arithmeticType, int which )
        {
            Item2D world;
            Scenario currentScenario;

            currentScenario = client.getSession().getCurrentScenario();
            world = currentScenario.getWorld();
            LinkedList<Item> itemList = world.getItemList();

            int index;
            
            Area tempLaneArea = null;

            // Make a pass to create all lane intersection.
            Area laneArithmeticArea;
            if( arithmeticType == 1 )
            {
                laneArithmeticArea = new Area( new Rectangle( 0, 0, videoDisplay.getRealWidth(), videoDisplay.getRealHeight() ) );
            }
            else
            {
                laneArithmeticArea = new Area();
            }

            for( index = 0; index < itemList.size(); index++ )
            {
                Item item = itemList.get( index );

                if( item instanceof Lane )
                {
                    Lane lane = ( Lane )item;

                    // If which is set to 1, just look at roads (no lanes).
                    if( which == 1 )
                    {
                        if( lane.getType().equals( Lane.TYPE_VERTICAL_SHOULDER ) )
                        {
                            continue;
                        }
                        else
                        if( lane.getType().equals( Lane.TYPE_HORIZONTAL_SHOULDER ) )
                        {
                            continue;
                        }
                    }
                    else
                    if( which == 2 )
                    {
                        if( lane.getType().equals( Lane.TYPE_VERTICAL ) )
                        {
                            continue;
                        }
                        else
                        if( lane.getType().equals( Lane.TYPE_HORIZONTAL ) )
                        {
                            continue;
                        }
                    }
                    else
                    if( which == 3 )    // Just vertical roads.
                    {
                        if( !lane.getType().equals( Lane.TYPE_VERTICAL ) )
                        {
                            continue;
                        }
                    }
                    else
                    if( which == 4 )    // Just horizontal roads.
                    {
                        if( !lane.getType().equals( Lane.TYPE_HORIZONTAL ) )
                        {
                            continue;
                        }
                    }

                    lane.fit( videoDisplay.getRealWidth(), videoDisplay.getRealHeight() );  // First time on this?

                    int tempLaneXPosition = lane.getXPosition();
                    int tempLaneYPosition = lane.getYPosition();
                    int tempLaneWidth = lane.getWidth();
                    int tempLaneHeight = lane.getHeight();

                    Polygon tempLanePolygon = new Polygon();

                    tempLanePolygon.addPoint( tempLaneXPosition, tempLaneYPosition );
                    tempLanePolygon.addPoint( tempLaneXPosition + tempLaneWidth, tempLaneYPosition );
                    tempLanePolygon.addPoint( tempLaneXPosition + tempLaneWidth, tempLaneYPosition + tempLaneHeight );
                    tempLanePolygon.addPoint( tempLaneXPosition, tempLaneYPosition + tempLaneHeight );

                    tempLaneArea = new Area( tempLanePolygon );

                    // Determine the operation.
                    if( arithmeticType == 0 )
                    {
                        laneArithmeticArea.add( tempLaneArea );
                    }
                    else
                    if( arithmeticType == 1 )
                    {
                        laneArithmeticArea.intersect( tempLaneArea );
                    }
                    else
                    //if( arithmeticType == 2 )
                    {
                        laneArithmeticArea.subtract( tempLaneArea );
                    }
                }
            }

            return( laneArithmeticArea );
        }

        /**
         * Draw a single lane.
         * @param lane which lane to draw.
         */
        public void drawLane( Lane lane )
        {
            if( lane.getType().equals( Lane.TYPE_VERTICAL ) )
            {
                videoDisplay.setPrintColor( colorList[COLOR_BLACK] );
                videoDisplay.drawRectangle( lane.getXPosition(), 0, lane.getXPosition() + lane.getWidth(), videoDisplay.getRealHeight() );
            }
            else
            if( lane.getType().equals( Lane.TYPE_HORIZONTAL ) )
            {
                videoDisplay.setPrintColor( colorList[COLOR_BLACK] );
                videoDisplay.drawRectangle( 0, lane.getYPosition(), videoDisplay.getRealWidth(), lane.getYPosition() + lane.getHeight() );
            }
            else
            if( lane.getType().equals( Lane.TYPE_VERTICAL_SHOULDER ) )
            {
                videoDisplay.setPrintColor( colorList[COLOR_LIGHT_GREY] );
                videoDisplay.drawRectangle( lane.getXPosition(), 0, lane.getXPosition() + lane.getWidth(), videoDisplay.getRealHeight() );
            }
            else
            if( lane.getType().equals( Lane.TYPE_HORIZONTAL_SHOULDER ) )
            {
                videoDisplay.setPrintColor( colorList[COLOR_LIGHT_GREY] );
                videoDisplay.drawRectangle( 0, lane.getYPosition(), videoDisplay.getRealWidth(), lane.getYPosition() + lane.getHeight() );
            }
        }

        /**
         * Draw the lines between lanes (or on the borders), depending on type.
         * NOTE: This could be massively overhauled to per a Lane object, utilizing
         * unused properties like direction and adjancency.
         * @param type
         */
        public void drawLaneLinesByType( String type )
        {
            Item2D world;
            Scenario currentScenario;

            currentScenario = client.getSession().getCurrentScenario();
            world = currentScenario.getWorld();
            LinkedList<Item> itemList = world.getItemList();

            int index;

            for( index = 0; index < itemList.size(); index++ )
            {
                Item item = itemList.get( index );

                if( item instanceof Lane )
                {
                    if( !item.getType().equals( type ) )
                    {
                        continue;
                    }

                    Lane lane = ( Lane )item;

                    drawLaneLines( lane );
                }
            }
        }

        /**
         * Method that calls the appropriate lane line drawing per Lane object passed.
         * @param lane particular Lane object used to draw the appropriate lines.
         */
        public void drawLaneLines( Lane lane )
        {
            videoDisplay.setPrintColor( colorList[COLOR_WHITE] );

            if( lane.getType().equals( Lane.TYPE_VERTICAL ) )
            {
                videoDisplay.drawLine( lane.getXPosition(), 0, lane.getXPosition(), videoDisplay.getRealHeight() );
                videoDisplay.drawLine( lane.getXPosition() + lane.getWidth() - 1, 0, lane.getXPosition() + lane.getWidth() - 1, videoDisplay.getRealHeight() );
            }
            else
            if( lane.getType().equals( Lane.TYPE_HORIZONTAL ) )
            {
                videoDisplay.drawLine( 0, lane.getYPosition(), videoDisplay.getRealWidth(), lane.getYPosition() );
                videoDisplay.drawLine( 0, lane.getYPosition() + lane.getHeight() - 1, videoDisplay.getRealWidth(), lane.getYPosition() + lane.getHeight() - 1 );
            }
            else
            if( lane.getType().equals( Lane.TYPE_VERTICAL_SHOULDER ) )
            {
                videoDisplay.drawLine( lane.getXPosition(), 0, lane.getXPosition(), videoDisplay.getRealHeight() );
                videoDisplay.drawLine( lane.getXPosition() + lane.getWidth() - 1, 0, lane.getXPosition() + lane.getWidth() - 1, videoDisplay.getRealHeight() );
            }
            else
            if( lane.getType().equals( Lane.TYPE_HORIZONTAL_SHOULDER ) )
            {
                videoDisplay.drawLine( 0, lane.getYPosition(), videoDisplay.getRealWidth(), lane.getYPosition() );
                videoDisplay.drawLine( 0, lane.getYPosition() + lane.getHeight() - 1, videoDisplay.getRealWidth(), lane.getYPosition() + lane.getHeight() - 1 );
            }
        }

        /**
         * Draw all the cars in the current world / scene.
         */
        public void drawCars()
        {
            int carIndex;
            VideoSprite carSprite;
            Automobile car;
            Item2D world;
            Scenario currentScenario;

            currentScenario = client.getSession().getCurrentScenario();
            world = currentScenario.getWorld();
            LinkedList<Item> itemList = world.getItemList();

            for( carIndex = 0; carIndex < itemList.size(); carIndex++ )
            {
                Item item = itemList.get( carIndex );

                if( item instanceof Automobile )
                {
                    car = ( Automobile )item;
                }
                else
                {
                    continue;
                }

                // Draw actual graphics.
                int carRoleId = car.getRoleId();

                if( carRoleId == Role.NONE )
                {
                    carSprite = carSpriteList.get( Role.NUMBER_OF_ROLES + 0 );
                    carSprite.setColor( colorList[car.getInstance()] );
                }
                else
                if( carRoleId == Role.VICTIM )
                {
                    carSprite = carSpriteList.get( Role.NUMBER_OF_ROLES + 0 );
                    carSprite.setColor( colorList[COLOR_WHITE] );
                }
                else
                {
                    carSprite = carSpriteList.get( carRoleId );
                }

                carSprite.setAngle( car.getAngle() );
                carSprite.setPosition( car.getXPosition(), car.getYPosition() );
                videoDisplay.draw( carSprite );

                // Draw the service patrol vehicle lights.
                if( carRoleId == Role.IDOT )
                {
                    lightSprite.setPosition(    car.getXPosition() - ( ( lightSprite.getWidth() - car.getWidth() ) / 2 ),
                                                car.getYPosition() + car.getHeight() );

                    int lightStatus = car.getLightStatus();
                    if( lightStatus != ServerConstants.LIGHT_STATE_OFF )
                    {
                        int frameIndex = lightSprite.getFrameIndex();
                        lightSprite.setAnimation( lightStatus );
                        lightSprite.setFrameIndex( frameIndex );

                        videoDisplay.draw( lightSprite );
                    }
                }

                // Check if this car is on fire.
                if( car.isOnFire() )
                {
                    fireAnimationImage.setPosition(    car.getXPosition() + ( ( car.getWidth() - fireAnimationImage.getWidth() ) / 4 ),
                                                       car.getYPosition() + ( ( car.getHeight() - fireAnimationImage.getHeight() ) / 4 ) );

                    videoDisplay.draw( fireAnimationImage );
                }

                // Print this car's instance number.
                videoDisplay.setPrintSize( 18 );
                videoDisplay.setPrintColor( colorList[COLOR_BLACK] );
                videoDisplay.print( ((Integer)car.getInstance()).toString() , car.getXPosition(), car.getYPosition() );
                videoDisplay.setPrintColor( colorList[COLOR_YELLOW] );
                videoDisplay.print( ((Integer)car.getInstance()).toString() , car.getXPosition() - 1, car.getYPosition() - 1 );

                // DEBUG: Show car item number (in grey).
                if( client.getDebugMode() )
                {
                    videoDisplay.setPrintColor( colorList[COLOR_BLACK] );
                    videoDisplay.print( ((Integer)car.getId()).toString() , car.getXPosition(), car.getYPosition() + 32 );
                    videoDisplay.setPrintColor( colorList[COLOR_LIGHT_GREY] );
                    videoDisplay.print( ((Integer)car.getId()).toString() , car.getXPosition() - 1, car.getYPosition() + 31 );
                }
            }
        }

        /**
         * Draw misc items, such as cones and what not (pretty much anything
         * that is not an Automobile or a Lane.
         */
        public void drawMiscItems()
        {
            int index;

            Item2D world;
            Scenario currentScenario;

            currentScenario = client.getSession().getCurrentScenario();
            world = currentScenario.getWorld();
            LinkedList<Item> itemList = world.getItemList();

            for( index = 0; index < itemList.size(); index++ )
            {
                Item item = itemList.get( index );

                if( !( item instanceof Lane ) && !( item instanceof Automobile ) && !( item instanceof Player ) )
                {
                    drawMiscItem( ( Item2D )item );
                }
                else
                {
                    continue;
                }
            }
        }

        /**
         * Draw a particular misc Item2D. Handles cones, signs, drums, and flares right now.
         * NOTE: Would be great to modularize this system and make it dynamic.
         * @param item2d actual item to draw.
         */
        public void drawMiscItem( Item2D item2d )
        {
            if( item2d.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.CONE] ) )
            {
                drawCone( item2d );
            }
            if( item2d.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.SIGN] ) )
            {
                drawSign( item2d );
            }
            if( item2d.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.DRUM] ) )
            {
                drawDrum( item2d );
            }
            if( item2d.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.FLARE] ) )
            {
                drawFlare( item2d );
            }

            // DEBUG: Print this item's item number beside it.
            if( client.getDebugMode() )
            {
                videoDisplay.setPrintSize( 18 );
                videoDisplay.setPrintColor( colorList[COLOR_BLACK] );
                videoDisplay.print( ((Integer)item2d.getId()).toString(), item2d.getXPosition(), item2d.getYPosition() );
                videoDisplay.setPrintColor( colorList[COLOR_YELLOW] );
                videoDisplay.print( ((Integer)item2d.getId()).toString(), item2d.getXPosition() - 1, item2d.getYPosition() - 1 );
            }
        }

        /**
         * Draw specifically a cone.
         * @param cone item to draw.
         */
        public void drawCone( Item2D cone )
        {
            coneSprite.setPosition( cone.getXPosition(), cone.getYPosition() );
            videoDisplay.draw( coneSprite );
        }

        /**
         * Draw specifically a cone.
         * @param cone item to draw.
         */
        public void drawSign( Item2D sign )
        {
            signSprite.setPosition( sign.getXPosition(), sign.getYPosition() );
            videoDisplay.draw( signSprite );
        }

        /**
         * Draw specifically a drum.
         * @param drum item to draw.
         */
        public void drawDrum( Item2D drum )
        {
            drumSprite.setPosition( drum.getXPosition(), drum.getYPosition() );
            videoDisplay.draw( drumSprite );
        }

        /**
         * Draw specifically a flare.
         * @param flare item to draw.
         */
        public void drawFlare( Item2D flare )
        {
            flareSprite.setPosition( flare.getXPosition(), flare.getYPosition() );
            videoDisplay.draw( flareSprite );
        }

        /**
         * Draws a temporary ghost image of the Item2D being dragged.
         */
        public void drawDraggedItem()
        {
            Item2D draggedItem = ( Item2D )client.getGUI().mouseDraggedItemReference;

            if( draggedItem == null )
            {
                return;
            }

            if( draggedItem instanceof Lane )
            {
                // Do nothing.
            }
            else
            if( draggedItem instanceof Automobile )
            {
                Automobile automobile = ( Automobile )draggedItem;

                VideoSprite carSprite = carSpriteList.get( automobile.getRoleId() );
                carSprite.setPosition( mouseCursor.getXPosition() - mouseCursorAndItemDelta.x, mouseCursor.getYPosition() - mouseCursorAndItemDelta.y );
                carSprite.setAngle( automobile.getAngle() );
                carSprite.setAlpha( 0.60f );
                videoDisplay.draw( carSprite );

                // Put graphics settings back to before.
                carSprite.setAlpha( 1.0f );
            }
            else
            if( draggedItem instanceof Item2D )
            {
                if( draggedItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.CONE] ) )
                {
                    coneSprite.setPosition( mouseCursor.getXPosition() - mouseCursorAndItemDelta.x, mouseCursor.getYPosition() - mouseCursorAndItemDelta.y );
                    coneSprite.setAlpha( 0.75f );
                    videoDisplay.draw( coneSprite );

                    // Put graphics settings back to before.
                    coneSprite.setAlpha( 1.0f );
                }
                else
                if( draggedItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.SIGN] ) )
                {
                    signSprite.setPosition( mouseCursor.getXPosition() - mouseCursorAndItemDelta.x, mouseCursor.getYPosition() - mouseCursorAndItemDelta.y );
                    signSprite.setAlpha( 0.75f );
                    videoDisplay.draw( signSprite );

                    // Put graphics settings back to before.
                    signSprite.setAlpha( 1.0f );
                }
                else
                if( draggedItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.DRUM] ) )
                {
                    drumSprite.setPosition( mouseCursor.getXPosition() - mouseCursorAndItemDelta.x, mouseCursor.getYPosition() - mouseCursorAndItemDelta.y );
                    drumSprite.setAlpha( 0.75f );
                    videoDisplay.draw( drumSprite );

                    // Put graphics settings back to before.
                    drumSprite.setAlpha( 1.0f );
                }
                else
                if( draggedItem.getType().equals( ServerConstants.ITEM_TYPE_NAME[ServerConstants.FLARE] ) )
                {
                    flareSprite.setPosition( mouseCursor.getXPosition() - mouseCursorAndItemDelta.x, mouseCursor.getYPosition() - mouseCursorAndItemDelta.y );
                    flareSprite.setAlpha( 0.75f );
                    videoDisplay.draw( flareSprite );

                    // Put graphics settings back to before.
                    flareSprite.setAlpha( 1.0f );
                }
            }
        }

        /**
         * Intended for drawing Item2D representation of movement.
         * Considered either drawing lines or animating a moving ghost image
         * (see drawDraggedItem()).
         */
        public void drawMovements()
        {
            
        }
}
